module Mtwo_dual_fifo_pre
#(
   parameter                RAM_TYPE   = "REG"
  ,parameter                ADDR_WIDTH = 1
  ,parameter                DATA_WIDTH = 1
  ,parameter                DATA_DEPTH = 1
)
(
  // clk and rst
   input                    i_clk
  ,input                    i_rst_n
  // write signal
  ,input                    i_wr
  ,input  [DATA_WIDTH-1:0]  i_wdata
  ,output                   o_full
  ,output [ADDR_WIDTH:0]    o_remain
  // read signal
  ,input                    i_rd
  ,output [DATA_WIDTH-1:0]  o_rdata
  ,output                   o_empty
  ,output [ADDR_WIDTH:0]    o_use
  // unusual
  ,output                   o_overflow
  ,output                   o_underflow
  ,output                   o_wrd_err
);

localparam FIFO_ADDR_WIDTH = ADDR_WIDTH - 1                 ;
localparam FIFO_DATA_WIDTH = DATA_WIDTH * 2                ;
localparam FIFO_DATA_DEPTH = DATA_DEPTH / 2 + DATA_DEPTH % 2;

//==========================================================
// ver                                            start//{{{
//==========================================================
reg                              rd_flag_r                 ;
reg       [1:0]                  wr_flag_r                 ;
reg       [1:0]                  wr_index_r                ;
reg                              wr_w                      ;
reg       [DATA_WIDTH-1:0]       rd_data_r                 ;
reg       [DATA_WIDTH-1:0]       wr_data_r[3:0]            ;

wire                             fifo_wr                   ;
wire      [FIFO_DATA_WIDTH-1:0]  fifo_wdata                ;
wire                             fifo_full                 ;
wire      [FIFO_ADDR_WIDTH:0]    fifo_remain               ;
wire                             fifo_rd                   ;
wire      [FIFO_DATA_WIDTH-1:0]  fifo_rdata                ;
wire                             fifo_empty                ;
wire      [FIFO_ADDR_WIDTH:0]    fifo_use                  ;
wire                             fifo_overflow             ;
wire                             fifo_underflow            ;
wire                             fifo_wrd_err              ;
//==========================================================
// ver                                              end//}}}
//==========================================================

//==========================================================
// write                                          start//{{{
//==========================================================
always@(posedge i_clk) begin
  if(i_rst_n == 1'd0) begin
    wr_flag_r <= 2'd0;
  end
  else  begin
    case({i_wr,wr_w})
    2'b00 : wr_flag_r <= wr_flag_r        ;
    2'b01 : wr_flag_r <= wr_flag_r - 2'd2 ;
    2'b10 : wr_flag_r <= wr_flag_r + 2'd1 ;
    2'b11 : wr_flag_r <= wr_flag_r - 2'd1 ;
    endcase
  end
end

always@(posedge i_clk) begin
  if(i_rst_n == 1'd0) begin
    wr_index_r <= 2'd0;
  end
  else if(i_wr) begin
    wr_index_r <= wr_index_r + 2'd1;
  end
end

always@(posedge i_clk) begin
  if(i_rst_n == 1'd0) begin
    wr_data_r[0] <= {DATA_WIDTH{1'd0}};
    wr_data_r[1] <= {DATA_WIDTH{1'd0}};
    wr_data_r[2] <= {DATA_WIDTH{1'd0}};
    wr_data_r[3] <= {DATA_WIDTH{1'd0}};
  end
  else if(i_wr) begin
    case(wr_index_r)
    2'd0 : wr_data_r[0] <= i_wdata;
    2'd1 : wr_data_r[1] <= i_wdata;
    2'd2 : wr_data_r[2] <= i_wdata;
    2'd3 : wr_data_r[3] <= i_wdata;
    endcase
  end
end

assign wr_w       = (wr_flag_r >= 2'd2) && (~fifo_rd)      ;

assign fifo_wr    = wr_w                                   ;
assign fifo_wdata = wr_index_r[1] ? {wr_data_r[1], wr_data_r[0]} : {wr_data_r[3], wr_data_r[2]};
assign o_full     = fifo_full                              ;
assign o_remain   = {fifo_remain, ~(|wr_flag_r)}           ;
//==========================================================
// write                                            end//}}}
//==========================================================

//==========================================================
// read                                           start//{{{
//==========================================================
always@(posedge i_clk) begin
  if(i_rst_n == 1'd0) begin
    rd_flag_r <= 1'd0;
  end
  else if(i_rd) begin
    rd_flag_r <= ~rd_flag_r;
  end
end

always@(posedge i_clk) begin
  if(i_rst_n == 1'd0) begin
    rd_data_r <= {DATA_WIDTH{1'd0}};
  end
  else if(i_rd && (~rd_flag_r)) begin
    rd_data_r <= fifo_rdata[FIFO_DATA_WIDTH-1:DATA_WIDTH];
  end
end

assign fifo_rd     = i_rd && (~rd_flag_r)                  ;
assign o_rdata     = rd_flag_r ? rd_data_r : fifo_rdata[DATA_WIDTH-1:0];
assign o_empty     = fifo_empty && (~rd_flag_r)            ;
assign o_use       = {fifo_use, rd_flag_r}                 ;
//==========================================================
// read                                             end//}}}
//==========================================================

assign o_overflow  = fifo_overflow                         ;
assign o_underflow = fifo_underflow                        ;
assign o_wrd_err   = fifo_wrd_err                          ;

//==========================================================
// fifo                                           start//{{{
//==========================================================
Msimple_fifo_pre
#(
   .RAM_TYPE                ( RAM_TYPE                     )
  ,.ADDR_WIDTH              ( FIFO_ADDR_WIDTH              )
  ,.DATA_WIDTH              ( FIFO_DATA_WIDTH              )
  ,.DATA_DEPTH              ( FIFO_DATA_DEPTH              )
)
M_simple_fifo_pre
(
  // clk and rst
   .i_clk                   ( i_clk                        )
  ,.i_rst_n                 ( i_rst_n                      )
  // write signal
  ,.i_wr                    ( fifo_wr                      )
  ,.i_wdata                 ( fifo_wdata                   )
  ,.o_full                  ( fifo_full                    )
  ,.o_remain                ( fifo_remain                  )
  // read signal
  ,.i_rd                    ( fifo_rd                      )
  ,.o_rdata                 ( fifo_rdata                   )
  ,.o_empty                 ( fifo_empty                   )
  ,.o_use                   ( fifo_use                     )
  // unusual
  ,.o_overflow              ( fifo_overflow                )
  ,.o_underflow             ( fifo_underflow               )
  ,.o_wrd_err               ( fifo_wrd_err                 )
);
//==========================================================
// fifo                                             end//}}}
//==========================================================

endmodule
